#pragma once

#include "Reactor.hpp"
#include "Util.hpp"
#include <cerrno>
#include <string>
#include <vector>




#define ONCE_SIZE 128
static int RecverCore(int sock, std::string &inbuffer)//第一个参数:表明从哪个套接字读  第二个参数:读取的内容放到哪里
{
    //因文件描述符都是被设置成非阻塞,并且工作模式是ET,所以要循环读取
    while (true)
    {
        char buffer[ONCE_SIZE];//一次读取的基本单位-多少个字节
        ssize_t s = recv(sock, buffer, ONCE_SIZE - 1, 0);
        if (s > 0)//读取成功 就一直读取
        {
            buffer[s] = '\0';
            inbuffer += buffer;//将读取到的数据添加到该套接字对应Event结构的inbuffer中
        }
        else if (s < 0)
        {
            if (errno == EINTR) // IO被信号打断，概率特别低
            {
                continue;
            }
            if (errno == EAGAIN || errno == EWOULDBLOCK)//数据读取完毕
            {
                return 1; // success
            }
            // 真的出错了
            return -1;
        }
        else // s == 0 对端关闭链接
        {
            return 0;
        }
    }
}


int Recver(Event *evp)
{
    std::cout << "Recver been called" << std::endl;

    // 1. 真正的读取-把数据读干净
    int result = RecverCore(evp->sock, evp->inbuffer);//从evp->sock里面读取,读取到inbuffer里

    if (result <= 0) //要么出错,要么对端关闭链接
    {
        //差错处理  -> 回调差错处理方法 evp->errorer不为空说明有该方法
        if (evp->errorer)
        {
            evp->errorer(evp);
        }
        return -1;
    }
    //  2. 分包-- 一个或者多个报文 -- 解决粘包问题
    std::vector<std::string> tokens;//把读取到的有效报文拆成一个一个的报文
    std::string sep = "X";//报文和报文的分隔符假设为sep
    SplitSegment(evp->inbuffer, &tokens, sep);//把evp->inbuffer的内容分包到tokens 分隔符为sep
    // 3. 反序列化 -- 针对一个报文 -- 提取有效参与计算或者存储的信息
    for (auto &seg : tokens)
    {
        struct data d;
        if (Deserialize(seg,  &d._x, &d._y, &d._op)) //对每一个报文进行反序列化
        {
            // 4. 业务逻辑 -- 得到结果
            int result = 0;
            switch (d._op)
            {
                case '+':
                    result = d._x + d._y;
                    break;
                case '-':
                    result = d._x - d._y;
                    break;
                case '*':
                    result = d._x * d._y;
                    break;
                case '/':
                    if (d._y == 0){
                        std::cerr << "Error: div zero!" << std::endl;
                        continue; //继续处理下一个报文
                    }
                    else{
                        result = d._x / d._y;
                    }
                    break;
                case '%':
                    if (d._y == 0){
                        std::cerr << "Error: mod zero!" << std::endl;
                        continue; //继续处理下一个报文
                    }
                    else{
                        result = d._x % d._y;
                    }
                    break;
                default:
                    std::cerr << "operation error!" << std::endl;
                    continue; //继续处理下一个报文
            }

            // 5. 构建响应 
            // 请求的报文:2+3X -> 响应的报文:2+3=5X
            std::string response;
            response += std::to_string(d._x);
            response += d._op;
            response += std::to_string(d._y);
            response += "=";
            response += std::to_string(result);
            response += sep; //报文与报文之间的分隔符!!协议大家都要遵守
            //将响应报文-- 添加到发送缓冲区outbuffer
            evp->outbuffer += response; 
        }
    }
    if (!(evp->outbuffer).empty())  //说明发送缓冲区有数据 
    {
        //此时要使能读写
        //写打开的时候,默认就是就绪的！即便是发送缓冲区已经满了
        //epoll策略规定,只要用户重新设置了EPOLLOUT事件,EPOLLOUT至少会在触发一次！
        evp->R->EnableRW(evp->sock, true, true);
    }
    return 0;
}



// 返回值说明:1: 全部将数据发送完成    0: 数据没有发完,但是不能再发了 -1: 发送失败
int SenderCore(int sock, std::string &outbuffer)//从哪个文件描述符发,发送到哪里
{
    while (true)
    {
        int total = 0; //本轮累计发送的数据量
        const char *start = outbuffer.c_str();
        int size = outbuffer.size();//期望发送的数据量
        ssize_t curr = send(sock, start + total, size - total, 0);
        if (curr > 0)
        {
            total += curr;
            if (total == size)   //全部将数据发送完成
            {
                outbuffer.clear();//把缓冲区的内容清掉
                return 1;
            }
        }
        else
        {
            if (errno == EINTR) //被信号中断,继续尝试写入
                continue;
            if (errno == EAGAIN || errno == EWOULDBLOCK)//说明底层发送缓冲区已经没有空间了
            {
                outbuffer.erase(0, total);//将已经发送的数据移出outbuffer
                return 0; //缓冲区写满,没写完
            }

            return -1; //发送失败
        }
    }
}


int Sender(Event *evp)
{
    std::cout << "Sender been called" << std::endl;

    // 返回值:1: 全部将数据发送完成 0: 数据没有发完，但是不能再发了  -1: 发送失败
    int result = SenderCore(evp->sock, evp->outbuffer);//发送的核心代码 从哪个文件描述符发,发送的数据
    if (result == 1) //全部发送完成了
    {
        evp->R->EnableRW(evp->sock, true, false); //按需设置  将读取保留,写入关掉,不再关心写事件
    } 
    else if (result == 0) //数据没有发完,但是不能再发了
    {
        //你可以什么都不做
        evp->R->EnableRW(evp->sock, true, true); //没有发送完毕,还需要继续关心写事件
    }
    else //失败了,写入出错
    {
        if (evp->errorer)//差错处理
        {
            evp->errorer(evp);
        }
    }
}

int Errorer(Event *evp)
{
    std::cout << "Errorer been called" << std::endl;
    evp->R->DeleteEvent(evp);//将该文件描述符从epoll模型中删除,并取消该文件描述符与其EventItem结构的映射关系
    return 0;
}